/*
 * -----------------------------------------------------------------------------
 * Project  : Oracle OCCI Support
 * Purpose  : Support Oracle OCCI connectivity.
 * File     : Win32.Data.OracleOcci.h
 * Author   : Eduardo Sobrino
 * Date     : Jul/23/2007
 */

#pragma once

#pragma region   // Include Files

#ifdef SUPPORT_ORACLE_OCCI_

#include <windows.h>
#include <occi.h>

#include "Win32.Data.DataSource.h"
#include "Win32.Diagnostics.Log.h"
#include "Win32.String.h"
#include "Win32.Types.h"
#include "Win32.MemoryItem.h"

#pragma endregion

// -----------------------------------------------------------------------------

namespace Kif
{
namespace Win32
{
namespace Data
{
namespace OracleOcciClient
{

// -----------------------------------------------------------------------------
#pragma region // General / Common Declarations / Definitions

enum OcciErrorNo
{
   OcciError_NoMoreDataAvailable      = -3,
   OcciError_FailedToCreateStatement  = -2,
   OcciError_Failed  = -1,
   OcciError_SUCCESS = 0,
   OcciError_CAPABILITY_NOT_SUPPORTED =   8,
   OcciError_OFEATURE_NOT_IMPLEMENTED =   9,
   OcciError_FATALERROR               = 990,
   OcciError_FATALERROR_FAILTOCONNECT = 991,
   OcciError_FATALERROR_COMMFAILURE   = 992
}  ;

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Declare OcciDatabase class

class OcciDatabase
{
private:
public:

   oracle::occi::Environment *pEnvironment;
   oracle::occi::Connection  *pDatabaseConnection;

   long        LastReturnCode;
   long        DatabaseErrorCode;
   Kif::Win32::String LastErrorMessage;

   long        ConnectionTimeout;               // connection timeout
   long        CommandTimeout;                  // command timeout
   bool        Available;
   bool        Connected;

   Kif::Win32::Diagnostics::Verbosity Verbose;

   void Dispose();
   OcciDatabase();  // (ctor)
  ~OcciDatabase()
   {
      Dispose();
   }

   inline void ResetError()
   {
      LastReturnCode = OcciError_SUCCESS;
      DatabaseErrorCode = OcciError_SUCCESS;
      LastErrorMessage.Dispose();
   }

   void SetError(oracle::occi::SQLException &ex);
   void SetError(std::exception &ex);
   int  GetError(Kif::Win32::String &errorMessage);

   inline bool Success()
   {
      return(LastReturnCode == OcciError_SUCCESS) ;
   }  ;

}  ;  // end of OcciDatabase class

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Declare OcciConnection class

class OcciConnection
{
private:
public:

   DataSourceInfo DataSource;   
   OcciDatabase   Database;

   bool Connect(DataSourceInfo &dataSource);
   void Disconnect();

   bool SetConnectionTimeout(long timeout);
   bool SetCommandTimeout(long timeout);

   #pragma region // Error Handling

   inline bool ErrorIsFatal()
   {  return(Database.DatabaseErrorCode >= OcciError_FATALERROR); }
   inline bool ErrorIsInCommunicationLink()
   {  return(Database.DatabaseErrorCode == OcciError_FATALERROR_COMMFAILURE); }
   inline long GetError()
   {  return(Database.DatabaseErrorCode); }
   inline long CompareError(long error)
   {  return(Database.DatabaseErrorCode == error); }

   #pragma endregion
   #pragma region // Transaction Management

   inline bool StartTransaction()
   {
      Database.LastReturnCode = OcciError_SUCCESS;
      return(Database.Success());
   }

   inline bool EndTransaction()
   {
      Database.LastReturnCode = OcciError_SUCCESS;
      return(Database.Success());
   }

   inline bool CommitAll()
   {
      Database.LastReturnCode = OcciError_SUCCESS;
      return(Database.Success());
   }

   inline bool RollbackAll()
   {
      Database.LastReturnCode = OcciError_SUCCESS;
      return(Database.Success());
   }

   #pragma endregion

}  ;  // end of OcciConnection class

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Declare Parameters/Columns Collections classes

typedef MemoryDataItem OcciParameter;
typedef MemoryDataItem OcciColumn;

typedef MemoryDataItemsCollection OcciParametersCollection;
typedef MemoryDataItemsCollection OcciColumnsCollection;

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Declare OdbcCommand class

class OcciDataReader;
class OcciCommand
{
private:
   oracle::occi::Statement *pStatement;
   oracle::occi::Statement::Status lastStatus;
   oracle::occi::ResultSet *pResultSet;

   bool createStatement(TCHAR *sqlStatementBuffer,size_t statementBufferLength);
   bool prepareQuery(TCHAR *commandText);
   long checkStatus(oracle::occi::Statement::Status status);

public:

   inline oracle::occi::ResultSet *GetResultSet()
   {
      return(pResultSet);
   }

   inline void Dispose()
   {
      if (pStatement != NULL)
         Connection->Database.pDatabaseConnection->
            terminateStatement(pStatement);
      pStatement = NULL;
   }

   inline oracle::occi::Statement *GetStatementHandle()
   {
      return(pStatement);
   }

   OcciCommand()
   {
      pStatement = NULL;
   }

  ~OcciCommand()
   {
      Dispose();
   }

   Kif::Win32::Data::CommandType CommandType;
   Kif::Win32::String CommandText;

   OcciConnection *Connection;
   OcciParametersCollection Parameters;
   OcciColumnsCollection Columns;

   OcciCommand(OcciConnection *connection,TCHAR *commandText,
      Kif::Win32::Data::CommandType commandType)
   {
      pStatement  = NULL;
      Connection  = connection;
      CommandText.Copy(commandText);
      CommandType = commandType;
   }  // end of OdbcCommand (ctor)

   OcciCommand(OcciConnection *connection, Kif::Win32::String &commandText,
      Kif::Win32::Data::CommandType commandType)
   {
      pStatement  = NULL;
      Connection  = connection;
      CommandText.Copy(commandText,commandText.GetLength());
      CommandType = commandType;
   }  // end of OdbcCommand (ctor)

   bool BindParameters(
      oracle::occi::Statement *pStatement,
      OcciParametersCollection &parameters);
   bool BindColumns(
      oracle::occi::Statement *pStatement,
      OcciColumnsCollection &columns);

   bool ExecuteNonQuery(Kif::Win32::String &commandText);
   bool ExecuteNonQuery()
   {
      return(ExecuteNonQuery(CommandText));
   }

   bool ExecutePreparedCommand();

   bool Prepare(TCHAR *commandText);
   bool Prepare(TCHAR *commandText, OcciColumnsCollection &columns);

   OcciDataReader *OpenReader(
      TCHAR *commandText, OcciColumnsCollection &columns);
   OcciDataReader *OpenReader(
      TCHAR *commandText,
      OcciParametersCollection &params, OcciColumnsCollection &columns);
   OcciDataReader *OpenReader()
   {
      return(OpenReader(CommandText.GetBuffer(),Parameters,Columns));
   }

}  ;  // end of OcciCommand

#pragma endregion
// -----------------------------------------------------------------------------
#pragma region // Declare OcciDataReader class

class OcciDataReader
{
private:
   OcciCommand *command;
   bool bof, eof;
   oracle::occi::ResultSet *pResultSet;

public:

   OcciDataReader(OcciCommand *command)
   {
      this->command = command;
      bof = true;
      eof = false;
      pResultSet = command->GetResultSet();
   }

   void Close()
   {
      command->Dispose();
   }

   inline void SetLastReturnCode()//SQLRETURN code)
   {
      //command->Connection->Database.LastReturnCode = code;
   }

   inline long GetLastReturnCode()
   {
      return(command->Connection->Database.LastReturnCode);
   }

   inline bool Success()
   {
      return(command->Connection->Database.Success());
   }

   inline void GetError()
   {
      //command->Connection->Database.GetError();
   }

   bool Read();

   Kif::Win32::String *AsString(DWORD index, Kif::Win32::String &column);

   /// <summary>Convert DD-MMM-YY string date as a DateTime</summary>
   /// <param name="column">column with formated date to convert</param>
   /// <remarks>caller must check that is not NULL before using it and must
   /// deallocate the DateTime (eg. delete datetime)</remarks>
   /// <returns>pointer to a DateTime</returns>
   inline Kif::Win32::DateTime *AsDate(Kif::Win32::String &column)
   {
      return Kif::Win32::DateTime::DD_MMM_YYYY_ToDate(column);
   }  // end of AsDate

}  ; // end of OdbcDataReader

#pragma endregion
// -----------------------------------------------------------------------------

}  // end of Kif::Win32::Data::OracleOcciClient
}  // end of Kif::Win32::Data
}  // end of Kif::Win32
}  // end of Kif

#endif
